﻿using System;
using UtilZ.Dotnet.Ex.Exceptions;
using UtilZ.Dotnet.Ex.Model;
using UtilZ.Dotnet.Ex.ZLog;

namespace UtilZ.Dotnet.Ex.DataStruct
{
    /// <summary>
    /// 引用类型数据拼接器,非线程安全.仅当数据类型为结构体时,才可调用指针参数相关的函数
    /// </summary>
    public class RefTypeDataMerger<T> where T : class
    {
        private readonly RefTypeDataMergerOptions _options;
        /// <summary>
        /// 获取实例初始化选项
        /// </summary>
        public RefTypeDataMergerOptions Options
        {
            get { return this._options; }
        }

        private T[] _buffer;
        private int _writeIndex = 0;
        private int _readIndex = 0;
        private int _dataLength = 0;

        /// <summary>
        /// 获取数据长度
        /// </summary>
        public int Length
        {
            get { return _dataLength; }
        }

        private T[] _taskTailHeadBuffer = null;




        /// <summary>
        /// 
        /// </summary>
        /// <param name="options">数据拼接器选项,为null使用默认值</param>
        public RefTypeDataMerger(RefTypeDataMergerOptions options = null)
        {
            if (options == null)
            {
                options = new RefTypeDataMergerOptions();
            }

            this._options = options;

            if (options.InitCapcity > 0)
            {
                this._buffer = new T[options.InitCapcity];
            }
            else
            {
                this._buffer = new T[1000];
            }
        }





        /// <summary>
        /// 追加数据
        /// </summary>
        /// <param name="buffer"></param>
        public void Append(T[] buffer)
        {
            if (buffer == null)
            {
                throw new ArgumentNullException(nameof(buffer));
            }

            this.Append(buffer, 0, buffer.Length);
        }

        /// <summary>
        /// 追加数据
        /// </summary>
        /// <param name="buffer"></param>
        /// <param name="offset"></param>
        /// <param name="length"></param>
        public void Append(T[] buffer, int offset, int length)
        {
            if (buffer == null)
            {
                throw new ArgumentNullException(nameof(buffer));
            }

            if (offset < 0 || offset >= buffer.Length)
            {
                throw new ArgumentOutOfRangeException(nameof(offset));
            }

            if (length < 0 || offset + length > buffer.Length)
            {
                throw new ArgumentOutOfRangeException(nameof(length));
            }

            if (this._buffer.Length - this._dataLength < length)
            {
                //数组剩余空间不足，需要扩容
                this.ExtendCapacity(ref offset, ref length);
            }

            int modLength = this._buffer.Length - this._writeIndex;
            if (modLength >= length)
            {
                //数据当前写索引之后空余空间长度足够
                Array.Copy(buffer, offset, this._buffer, this._writeIndex, length);
                this._writeIndex += length;
            }
            else
            {
                //数据当前写索引之后空余空间长度不够
                //先写空余空间
                Array.Copy(buffer, offset, this._buffer, this._writeIndex, modLength);

                //将写位置归0，重新从0开始写
                int modWriteLength = length - modLength;
                Array.Copy(buffer, offset + modLength, this._buffer, 0, modWriteLength);
                this._writeIndex = modWriteLength;
            }

            this._dataLength += length;
        }

        private void ExtendCapacity(ref int offset, ref int newDataLength)
        {
            int bufferLength = this._buffer.Length;
            int dropLength;
            if (bufferLength >= int.MaxValue ||
                (this._options.LimitCapcity > 0 && bufferLength >= this._options.LimitCapcity))
            {
                //当数组容量已达到上限时，则直接处理容器满，不再扩容
                if (newDataLength > bufferLength)
                {
                    offset = offset + newDataLength - bufferLength;
                    newDataLength = bufferLength;
                    this.Clear();
                }
                else
                {
                    dropLength = newDataLength - (bufferLength - this._dataLength);
                    this.ContainerFull(dropLength);
                }
            }
            else
            {
                //容量不够，需要扩容
                long newCapcity;
                if (bufferLength < this._options.Limit)
                {
                    //当缓存容量小于门限时，每次扩容直接乘以扩展系数
                    newCapcity = (long)(bufferLength * this._options.Factor);
                }
                else
                {
                    //当缓存容量大于门限时，每次扩容直接乘以门限扩展系数
                    newCapcity = (long)(bufferLength * this._options.LimitFactor);
                }

                long requireLength = (long)this._dataLength + (long)newDataLength;
                if (requireLength > newCapcity)
                {
                    //如果扩容后容量不够，则重新计算新容量
                    newCapcity = (long)(requireLength * this._options.LimitFactor);
                }

                if (this._options.LimitCapcity > 0 && newCapcity > this._options.LimitCapcity)
                {
                    //容量超过上限，则指定为上限
                    newCapcity = this._options.LimitCapcity;
                }
                else if (newCapcity > int.MaxValue)
                {
                    //当数组容量上限为int.MaxValue，则直接处理容器满，不再扩容
                    newCapcity = int.MaxValue;
                }

                if (requireLength > newCapcity)
                {
                    //处理容器满
                    if (newDataLength > newCapcity)
                    {
                        offset = (int)(offset + newDataLength - newCapcity);
                        newDataLength = (int)newCapcity;
                        this.Clear();
                    }
                    else
                    {
                        dropLength = (int)(requireLength - bufferLength);
                        this.ContainerFull(dropLength);
                    }
                }

                if (bufferLength >= newCapcity)
                {
                    return;
                }

                var newBuffer = new T[(int)newCapcity];
                if (this._dataLength > 0)
                {
                    int bufferReadModLength = this._buffer.Length - this._readIndex;
                    if (bufferReadModLength >= this._dataLength)
                    {
                        //旧数据全部在读索引之后
                        Array.Copy(this._buffer, this._readIndex, newBuffer, 0, this._dataLength);
                    }
                    else
                    {
                        //旧数据部分在读索引之后，一部分从0开始
                        Array.Copy(this._buffer, this._readIndex, newBuffer, 0, bufferReadModLength);
                        int oldModLength = this._dataLength - bufferReadModLength;
                        Array.Copy(this._buffer, 0, newBuffer, bufferReadModLength, oldModLength);
                    }

                    this._readIndex = 0;
                    this._writeIndex = this._dataLength;
                }
                this._buffer = newBuffer;
            }
        }

        private void ContainerFull(int dropLength)
        {
            switch (this._options.FullMode)
            {
                case ContainerFullMode.Exception:
                    throw new ContainerFullException();
                case ContainerFullMode.DropNew:
                    this.DropNew(dropLength);
                    break;
                case ContainerFullMode.DropOld:
                    this.DropOld(dropLength);
                    break;
                case ContainerFullMode.Ignore:
                    //直接返回即可
                    ZLoger.Warn($"{nameof(RefTypeDataMerger<T>)}忽略数据项");
                    return;
                default:
                    throw new NotImplementedException(this._options.FullMode.ToString());
            }
        }

        private void DropOld(int length)
        {
            int modLength = this._buffer.Length - this._readIndex;
            if (modLength >= length)
            {
                this._readIndex += length;
            }
            else
            {
                this._readIndex = length - modLength;
            }

            this._dataLength -= length;
        }

        private void DropNew(int length)
        {
            if (this._writeIndex >= length)
            {
                this._writeIndex -= length;
            }
            else
            {
                this._writeIndex = this._buffer.Length - (length - this._writeIndex);
            }

            this._dataLength -= length;
        }






        private ArraySpan<T> _dataSpan = null;
        /// <summary>
        /// 获取起始位置的数据
        /// </summary>
        /// <param name="length">获取数据长度</param>
        /// <returns></returns>
        /// <exception cref="ArgumentOutOfRangeException"></exception>
        public ArraySpan<T> TakeDataSpan(int length)
        {
            if (this._dataLength < length)
            {
                throw new ArgumentOutOfRangeException(nameof(length));
            }

            if (this._dataSpan == null)
            {
                this._dataSpan = new ArraySpan<T>();
            }

            int modLength = this._buffer.Length - this._readIndex;
            if (modLength >= length)
            {
                this._dataSpan.Update(this._buffer, this._readIndex, length);
                this._readIndex += length;
            }
            else
            {
                if (this._taskTailHeadBuffer == null || this._taskTailHeadBuffer.Length < length)
                {
                    this._taskTailHeadBuffer = new T[length];
                }

                //先读取数组后面的
                Array.Copy(this._buffer, this._readIndex, this._taskTailHeadBuffer, 0, modLength);

                //读取位置归0，再重头读取剩余长度的数据
                int modReadLength = length - modLength;
                Array.Copy(this._buffer, 0, this._taskTailHeadBuffer, modLength, modReadLength);
                this._readIndex = modReadLength;

                this._dataSpan.Update(this._taskTailHeadBuffer, 0, length);
            }

            this._dataLength -= length;
            return this._dataSpan;
        }

        /// <summary>
        /// 获取结束位置的数据
        /// </summary>
        /// <param name="length">获取数据长度</param>
        /// <returns></returns>
        /// <exception cref="ArgumentOutOfRangeException"></exception>
        public ArraySpan<T> TakeDataSpanTail(int length)
        {
            if (this._dataLength < length)
            {
                throw new ArgumentOutOfRangeException(nameof(length));
            }

            if (this._dataSpan == null)
            {
                this._dataSpan = new ArraySpan<T>();
            }

            if (this._writeIndex >= length)
            {
                this._writeIndex -= length;
                this._dataSpan.Update(this._buffer, this._writeIndex, length);
            }
            else
            {
                if (this._taskTailHeadBuffer == null || this._taskTailHeadBuffer.Length < length)
                {
                    this._taskTailHeadBuffer = new T[length];
                }

                int secondPartLength = this._writeIndex;
                int firstPartLength = length - secondPartLength;

                //先读取第二部分数据
                Array.Copy(this._buffer, 0, this._taskTailHeadBuffer, 0, secondPartLength);

                //再读取数组后面的
                this._writeIndex = this._buffer.Length - firstPartLength;
                Array.Copy(this._buffer, this._writeIndex, this._taskTailHeadBuffer, this._writeIndex, firstPartLength);

                this._dataSpan.Update(this._taskTailHeadBuffer, 0, length);
            }

            this._dataLength -= length;
            return this._dataSpan;
        }




        /// <summary>
        /// 获取起始位置的数据
        /// </summary>
        /// <param name="length">获取数据长度</param>
        /// <returns>数据</returns>
        public T[] Take(int length)
        {
            if (this._dataLength < length)
            {
                throw new ArgumentOutOfRangeException(nameof(length));
            }

            var data = new T[length];

            int modLength = this._buffer.Length - this._readIndex;
            if (modLength >= length)
            {
                Array.Copy(this._buffer, this._readIndex, data, 0, length);
                this._readIndex += length;
            }
            else
            {
                //先读取数组后面的
                Array.Copy(this._buffer, this._readIndex, data, 0, modLength);

                //读取位置归0，再重头读取剩余长度的数据
                int modReadLength = length - modLength;
                Array.Copy(this._buffer, 0, data, modLength, modReadLength);
                this._readIndex = modReadLength;
            }

            this._dataLength -= length;
            return data;
        }

        /// <summary>
        /// 获取结束位置的数据
        /// </summary>
        /// <param name="length">获取数据长度</param>
        /// <returns>数据</returns>
        public T[] TakeTail(int length)
        {
            if (this._dataLength < length)
            {
                throw new ArgumentOutOfRangeException(nameof(length));
            }

            var data = new T[length];

            if (this._writeIndex >= length)
            {
                this._writeIndex -= length;
                Array.Copy(this._buffer, this._writeIndex, data, 0, length);
            }
            else
            {
                int secondPartLength = this._writeIndex;
                int firstPartLength = length - secondPartLength;

                //先读取第二部分数据
                Array.Copy(this._buffer, 0, data, 0, secondPartLength);

                //再读取数组后面的
                this._writeIndex = this._buffer.Length - firstPartLength;
                Array.Copy(this._buffer, this._writeIndex, data, this._writeIndex, firstPartLength);
            }

            this._dataLength -= length;
            return data;
        }





        /// <summary>
        /// 获取起始位置的数据
        /// </summary>
        /// <param name="destinationArray">目标数组</param>
        /// <param name="destinationIndex">目标数组起始位置</param>
        /// <param name="length">获取数据长度</param>
        /// <exception cref="ArgumentOutOfRangeException">参数越界异常</exception>
        public void Take(T[] destinationArray, int destinationIndex, int length)
        {
            if (this._dataLength < length)
            {
                throw new ArgumentOutOfRangeException(nameof(length));
            }

            if (destinationArray == null)
            {
                throw new ArgumentNullException(nameof(destinationArray));
            }

            if (destinationIndex >= destinationArray.Length)
            {
                throw new ArgumentOutOfRangeException(nameof(destinationIndex));
            }

            long endIndex = (long)destinationIndex + (long)length;
            if (endIndex > destinationArray.Length)
            {
                throw new ArgumentOutOfRangeException(nameof(length));
            }

            int modLength = this._buffer.Length - this._readIndex;
            if (modLength >= length)
            {
                Array.Copy(this._buffer, this._readIndex, destinationArray, destinationIndex, length);
                this._readIndex += length;
            }
            else
            {
                //先读取数组后面的
                Array.Copy(this._buffer, this._readIndex, destinationArray, destinationIndex, modLength);
                destinationIndex += modLength;

                //读取位置归0，再重头读取剩余长度的数据
                int modReadLength = length - modLength;
                Array.Copy(this._buffer, 0, destinationArray, destinationIndex, modReadLength);
                this._readIndex = modReadLength;
            }

            this._dataLength -= length;
        }

        /// <summary>
        /// 获取结束位置的数据
        /// </summary>
        /// <param name="destinationArray">目标数组</param>
        /// <param name="destinationIndex">目标数组起始位置</param>
        /// <param name="length">获取数据长度</param>
        /// <exception cref="ArgumentOutOfRangeException">参数越界异常</exception>
        public void TakeTail(T[] destinationArray, int destinationIndex, int length)
        {
            if (this._dataLength < length)
            {
                throw new ArgumentOutOfRangeException(nameof(length));
            }

            if (destinationArray == null)
            {
                throw new ArgumentNullException(nameof(destinationArray));
            }

            if (destinationIndex >= destinationArray.Length)
            {
                throw new ArgumentOutOfRangeException(nameof(destinationIndex));
            }

            long endIndex = (long)destinationIndex + (long)length;
            if (endIndex > destinationArray.Length)
            {
                throw new ArgumentOutOfRangeException(nameof(length));
            }

            if (this._writeIndex >= length)
            {
                this._writeIndex -= length;
                Array.Copy(this._buffer, this._writeIndex, destinationArray, destinationIndex, length);
            }
            else
            {
                int secondPartLength = this._writeIndex;
                int firstPartLength = length - secondPartLength;

                //先读取第二部分数据
                Array.Copy(this._buffer, 0, destinationArray, destinationIndex, secondPartLength);

                //再读取数组后面的
                this._writeIndex = this._buffer.Length - firstPartLength;
                Array.Copy(this._buffer, this._writeIndex, destinationArray, this._writeIndex + destinationIndex, firstPartLength);
            }

            this._dataLength -= length;
        }






        /// <summary>
        /// 弹出起始位置的数据,数据长度不改变
        /// </summary>
        /// <param name="destinationArray">目标数组</param>
        /// <param name="destinationIndex">目标数组起始位置</param>
        /// <param name="length">获取数据长度</param>
        /// <exception cref="ArgumentOutOfRangeException">参数越界异常</exception>
        public void Peek(T[] destinationArray, int destinationIndex, int length)
        {
            if (this._dataLength < length)
            {
                throw new ArgumentOutOfRangeException(nameof(length));
            }

            if (destinationArray == null)
            {
                throw new ArgumentNullException(nameof(destinationArray));
            }

            if (destinationIndex >= destinationArray.Length)
            {
                throw new ArgumentOutOfRangeException(nameof(destinationIndex));
            }

            long endIndex = (long)destinationIndex + (long)length;
            if (endIndex > destinationArray.Length)
            {
                throw new ArgumentOutOfRangeException(nameof(length));
            }

            int modLength = this._buffer.Length - this._readIndex;
            if (modLength >= length)
            {
                Array.Copy(this._buffer, this._readIndex, destinationArray, destinationIndex, length);
            }
            else
            {
                //先读取数组后面的
                Array.Copy(this._buffer, this._readIndex, destinationArray, destinationIndex, modLength);
                destinationIndex += modLength;

                //读取位置归0，再重头读取剩余长度的数据
                int modReadLength = length - modLength;
                Array.Copy(this._buffer, 0, destinationArray, destinationIndex, modReadLength);
            }
        }

        /// <summary>
        /// 弹出结束位置的数据,数据长度不改变
        /// </summary>
        /// <param name="destinationArray">目标数组</param>
        /// <param name="destinationIndex">目标数组起始位置</param>
        /// <param name="length">获取数据长度</param>
        /// <exception cref="ArgumentOutOfRangeException">参数越界异常</exception>
        public void PeekTail(T[] destinationArray, int destinationIndex, int length)
        {
            if (this._dataLength < length)
            {
                throw new ArgumentOutOfRangeException(nameof(length));
            }

            if (destinationArray == null)
            {
                throw new ArgumentNullException(nameof(destinationArray));
            }

            if (destinationIndex >= destinationArray.Length)
            {
                throw new ArgumentOutOfRangeException(nameof(destinationIndex));
            }

            long endIndex = (long)destinationIndex + (long)length;
            if (endIndex > destinationArray.Length)
            {
                throw new ArgumentOutOfRangeException(nameof(length));
            }

            if (this._writeIndex >= length)
            {
                Array.Copy(this._buffer, this._writeIndex - length, destinationArray, destinationIndex, length);
            }
            else
            {
                //先读取第二部分数据
                Array.Copy(this._buffer, 0, destinationArray, destinationIndex, this._writeIndex);

                //再读取数组后面的
                int firstPartLength = length - this._writeIndex;
                var writeIndex = this._buffer.Length - firstPartLength;
                Array.Copy(this._buffer, writeIndex, destinationArray, writeIndex + destinationIndex, firstPartLength);
            }

            this._dataLength -= length;
        }





        /// <summary>
        /// 移除位于始位位置指定长度的数据
        /// </summary>
        /// <param name="length">要忽略的数据长度</param>
        public void Remove(int length)
        {
            if (this._dataLength < length)
            {
                throw new ArgumentOutOfRangeException(nameof(length));
            }

            int modLength = this._buffer.Length - this._readIndex;
            if (modLength >= length)
            {
                this._readIndex += length;
            }
            else
            {
                //读取位置归0，再重头读取剩余长度的数据
                this._readIndex = length - modLength;
            }

            this._dataLength -= length;
        }

        /// <summary>
        /// 移除位于尾部位置指定长度的数据
        /// </summary>
        /// <param name="length">要忽略的数据长度</param>
        public void RemoveTail(int length)
        {
            if (this._dataLength < length)
            {
                throw new ArgumentOutOfRangeException(nameof(length));
            }

            if (this._writeIndex >= length)
            {
                this._writeIndex -= length;
            }
            else
            {
                //再读取数组后面的
                this._writeIndex = this._buffer.Length - (length - this._writeIndex);
            }

            this._dataLength -= length;
        }





        /// <summary>
        /// 清空数据
        /// </summary>
        public void Clear()
        {
            this._readIndex = 0;
            this._writeIndex = 0;
            this._dataLength = 0;
        }


    }


}
